package librpc

import (
	"kumachan/standalone/rx"
	"kumachan/standalone/rpc"
	"kumachan/standalone/rpc/vd"
	"kumachan/interpreter/runtime/rt"
)


type AbstractMachine interface {
	Call(ctx *rx.Context, f rt.FuncValue, arg Value) Value
	MustGetFuncValue(sym rt.Symbol) rt.FuncValue
}

func CreateVdRpcApi(conf rt.RpcConf, m AbstractMachine) (*VdApiImpl, *RpcApiImpl) {
	var vd_api = CreateVdApi(conf.VdConf, m)
	var services = make(ServiceIndex)
	for id, item := range conf.RpcServiceInfoIndex {
		services[id] = &rt.RpcService {
			Interface:     item.Interface,
			MethodsImpl:   implementMethods(item.MethodIndex, m),
			DispatchTable: craftDispatchTable(item.MethodIndex),
		}
	}
	return vd_api, &RpcApiImpl {
		VdApiImpl:   vd_api,
		ServiceIndex: services,
	}
}

func CreateVdApi(conf rt.VdConf, m AbstractMachine) *VdApiImpl {
	var context = createVdTransformContext(conf, m)
	var transformer = makeVdTransformer(context)
	return &VdApiImpl {
		context:     context,
		transformer: transformer,
	}
}

func createVdTransformContext(conf rt.VdConf, m AbstractMachine) *vdTransformContext {
	var validators = make(map[vd.ValidatorId] func(vd.Object)(bool))
	var adapters = make(map[vd.AdapterId] func(vd.Object)(vd.Object))
	for id, item := range conf.VdValidatorTable {
		var f = m.MustGetFuncValue(item.Symbol)
		validators[id] = func(obj vd.Object) bool {
			var ret = m.Call(rx.Background(), f, obj)
			return rt.FromBool(ret.(rt.EnumValue))
		}
	}
	for id, item := range conf.VdAdapterTable {
		var f = m.MustGetFuncValue(item.Symbol)
		adapters[id] = func(obj vd.Object) vd.Object {
			return m.Call(rx.Background(), f, obj)
		}
	}
	return &vdTransformContext {
		config:  conf,
		machine: m,
		vdUtilityFunctions: vdUtilityFunctions {
			validators: validators,
			adapters:   adapters,
		},
	}
}

func implementMethods(methods_def (map[string] uint), m AbstractMachine) (map[string] rpc.ServiceMethodImpl) {
	var methods = make(map[string] rpc.ServiceMethodImpl)
	for name, index := range methods_def {
		var index = index
		methods[name] = func(instance vd.Object, arg vd.Object) rx.Observable {
			var iv = instance.(rt.InterfaceValue)
			var f = iv.DispatchTable.Methods[index]
			var v = m.Call(rx.Background(), f, iv.ConcreteValue).(rt.FuncValue)
			return m.Call(rx.Background(), v, arg).(rx.Observable)
		}
	}
	return methods
}

func craftDispatchTable(methods_def (map[string] uint)) *rt.DispatchTable {
	var methods = make([] rt.FuncValue, len(methods_def))
	for name, index := range methods_def {
		var name = name
		methods[index] = rt.ValFuncNative(func(instance_ Value, _ rt.InteropContext) Value {
			var instance = instance_.(*rpc.ClientInstance)
			return rt.ValFuncNative(func(arg Value, _ rt.InteropContext) Value {
				return instance.Call(name, arg)
			})
		})
	}
	return &rt.DispatchTable {
		Methods:  methods,
		Included: [] *rt.DispatchTable {},
	}
}


